﻿/*---------------------------------------------------------------------------
合并多个二进制文件
读取配置文件, 根据配置文件中的信息，合并输出文件

配置文件格式如下：
#// 空隙填充值
{pad=[0x00]}

#// 输出文件
{outfile=[kernel.bin]}

#// 输入文件、偏移量(最多20个文件)
{[inputfile=[head.bin],          offset=[0x00000000]}
{[inputfile=[uImage],            offset=[0x00100000]}
{[inputfile=[uramdisk.image.gz], offset=[0x00400000]}
{[inputfile=[devicetree.dtb],    offset=[0x00800000]}
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <time.h>

#pragma hdrstop

#include <tchar.h>
#pragma argsused
//---------------------------------------------------------------------------
#define debug(...)  //do{printf("[line:%d] ", __LINE__, __func__); printf(__VA_ARGS__); }while(0)
#define debug1(...) //do{printf("[line:%d] ", __LINE__, __func__); printf(__VA_ARGS__); }while(0)

#define NAME_MAX_SIZE 256

// 文件信息
typedef struct file_info_t
{
    int  index;
    uint32_t offset;
    char name[NAME_MAX_SIZE];
    int  size;
    int  flag;
} file_info_t;

char  pad = 0;
file_info_t output_file     = {0};      // 输出文件
file_info_t input_files[20] = {{0}};  // 输入文件
int files_cnts = 0;        // 输入文件个数


// 解析一行
int parse_line(char buffer[])
{
    int  ret;
    char *pTmp;
    char *pTail;
    char tmp_string[1024];
    int line_size;

    char *pHead = buffer;
    // 搜索注释符号 #
    pTmp = strchr(buffer, '#');
    if (pTmp)
    {
        *pTmp = '\0';
    }
    line_size = strlen(buffer);
    if (line_size < 5)
    {
        return -1;
    }
    debug("++%s \n\r", buffer);


    // 搜索 "pad"
    pTmp = strstr(pHead, "pad");
    if (pTmp)
    {
        pTmp += 4;
        memset(tmp_string, 0, sizeof(tmp_string));
        ret = sscanf(pTmp, "[%s]", tmp_string);
        if (ret > 0)
        {
            pad = (char)strtoul(tmp_string, NULL, 0);
            debug("pad = 0x%02x\r\n\r\n", (uint32_t)(uint8_t)pad);
            return 0;
        }
        return -1;
    }

    // 搜索 "outfile=[kernel.bin]"
    pTmp = strstr(pHead, "outfile");
    if (pTmp)
    {
        pTmp += 8;
        pTmp = strchr(pTmp, '[');
        if (pTmp == NULL)
            return -1;

        pTmp++;
        pTail = strchr(pTmp, ']');
        if (pTail == NULL)
            return -1;

        *pTail = '\0';
        memset(output_file.name, 0, NAME_MAX_SIZE);
        strcpy(output_file.name, pTmp);
        output_file.flag = 1;
        debug("outfile = %s\r\n\r\n", output_file.name);
        return 0;
    }

    // 搜索 {[inputfile=[head.bin],offset=[0x00000000]}
    pTmp = strstr(pHead, "inputfile");
    if (pTmp)
    {
        pTmp += 10;
        pTmp = strchr(pTmp, '[');
        if (pTmp == NULL) 
            return -1;

        pTmp++;
        pTail = strchr(pTmp, ']');
        if (pTail == NULL)
            return -1;

        *pTail = '\0';
        memset(input_files[files_cnts].name, 0, NAME_MAX_SIZE);
        strcpy(input_files[files_cnts].name, pTmp);
        pTmp = pTail + 1;
        pTmp = strstr(pTmp, "offset");
        if (pTmp)
        {
            pTmp += 7;
            pTmp = strchr(pTmp, '[');
            if (pTmp == NULL)
                return -1;

            pTmp++;
            pTail = strchr(pTmp, ']');
            if (pTail == NULL)
                return -1;

            *pTail = '\0';
            input_files[files_cnts].offset = strtoul(pTmp, NULL, 0);
            input_files[files_cnts].index = files_cnts;
            input_files[files_cnts].flag = 1;
            debug("index=%d,offset=0x%08x,inputfile=%s\r\n\r\n",
                  input_files[files_cnts].index,
                  input_files[files_cnts].offset,
                  input_files[files_cnts].name);

            files_cnts++;
            return 0;
        }
        return -1;
    }

    return -1;
}

//读取配置文件config.txt,解析数据
int parse_config_file(void)
{
    //int ret;
    char *pString = NULL;
    char buffer[2048];

    FILE *pfConfigFile = fopen("bin2one.ini", "rb");
    if (pfConfigFile == NULL)
    {
        printf("not find bin2one.ini\n\r");
        return -1;
    }

    while (1)
    {
        // 读取一行
        memset(buffer, 0, sizeof(buffer));
        //ret = fscanf(pfConfigFile, "%s\r\n", buffer);
        pString = fgets(buffer, sizeof(buffer), pfConfigFile);
        if (pString == NULL)
        {
            break;  // 文件结束
        }
        pString = strrchr(buffer, '}');
        if (pString == NULL) 
            continue;
        *pString = '\0';

		// 解析一行
		debug("%s\r\n", buffer);
        parse_line(buffer);
	}

	return 0;
}

int file_size(char *file)
{
    //FILE *pFile;
    //int size;

    FILE *pFile = fopen(file,"r");
    if (!pFile)
    {
        return -1;
    }

    fseek(pFile, 0, SEEK_END);
    int size = ftell(pFile);
    fclose(pFile);
    return size;
}

int check_all_file(void)
{
    int i;
    int ret;
    debug1("total files: %d\n\r", files_cnts);

    // 检测输入文件是否存在
    for (i = 0; i < files_cnts; i++)
    {
        ret = file_size(input_files[i].name);
        if (ret < 0)
        {
            printf("error: not find file %s\n\r", input_files[i].name);
            return -1;
        }
        input_files[i].size = ret;
        debug1("index=%d,offset=0x%08x,inputfile=%s,size=%d\n\r",
               input_files[i].index,
               input_files[i].offset,
               input_files[i].name,
               input_files[i].size);
    }

    // 检测,计算输出文件
    if (output_file.flag == 0)
    {
        printf("error: not [outfile] in config.txt\n\r");
        return -1;
    }
    for (i = 0; i < files_cnts; i++)
    {
        ret = input_files[i].offset + input_files[i].size;
        if (output_file.size < ret)
        {
            output_file.size = ret;
        }
    }
    debug1("outfile=%s,size=%d\n\r",  output_file.name,  output_file.size);
    return 0;
}

int bin2one(void)
{
    //int i;
    //int ret = -1;
    int size;
    char *pBuffer = NULL;
    FILE *pFile;
    uint32_t offset;


    int ret = check_all_file();
    if (ret < 0)
    {
        return -1;
    }

    // 合并文件
    size = output_file.size;
    pBuffer =  (char*)malloc(size);
    if (NULL == pBuffer)
    {
        printf("malloc() error\n\r");
        return -1;
    }
    memset(pBuffer, pad, size);   // 填充buffer

    // 读取文件
    for (int i = 0; i < files_cnts; i++)
    {
        pFile = fopen(input_files[i].name, "rb");
        if (pFile == NULL)
        {
            printf("error: fopen(%s)\n\r", input_files[i].name);
            return -1;
        }
        offset = input_files[i].offset;
        ret = fread(&pBuffer[offset], input_files[i].size, 1, pFile);
        if (ret < 1)
        {
            printf("error: fread(%s)\n\r", input_files[i].name);
            fclose(pFile);
            return -1;
        }
        fclose(pFile);
    }

    // 写入文件
    pFile = fopen(output_file.name, "wb");
    if (pFile == NULL)
    {
        printf("error: fopen(%s)\n\r", output_file.name);
        return -1;
    }
    ret = fwrite(pBuffer, size, 1, pFile);
    if (ret < 1)
    {
        printf("error: fwrite(%s)\n\r", output_file.name);
        fclose(pFile);
        return -1;
    }
    fclose(pFile);
    return 0;
}


// main() 函数
int _tmain(int argc, _TCHAR *argv[])
{
    int ret;

    printf("starting ...\n\r");
    ret = parse_config_file();
    if (ret < 0)
    {
        printf(" error\n\r");
        return -1;
    }

    ret = bin2one();
    if (ret < 0)
    {
        printf(" error \n\r");
        return -1;
    }

    printf(" OK \n\r");
    return 0;
}


